package edu.cuit.zhuyimeng.flight.service.impl;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheGetResult;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.dynamic.datasource.annotation.Slave;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import edu.cuit.zhuyimeng.flight.controller.dto.QueryFlightDTO;
import edu.cuit.zhuyimeng.flight.controller.vo.FlightInfoVO;
import edu.cuit.zhuyimeng.flight.convert.FlightConverter;
import edu.cuit.zhuyimeng.flight.dal.mapper.FlightMapper;
import edu.cuit.zhuyimeng.flight.dal.po.FlightPO;
import edu.cuit.zhuyimeng.flight.enums.CacheConstants;
import edu.cuit.zhuyimeng.flight.service.IFlightQueryService;
import edu.cuit.zhuyimeng.framework.common.exception.QueryException;
import edu.cuit.zhuyimeng.framework.common.system.enums.FlightStatus;
import edu.cuit.zhuyimeng.framework.common.system.enums.SysConstants;
import edu.cuit.zhuyimeng.framework.common.util.TimeUtils;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * 航班查询服务
 */
@Service
@RequiredArgsConstructor
public class FlightQueryService implements IFlightQueryService {

    private final FlightMapper flightMapper;

    private final Cache<String, List<Long>> specificFlightInfoCache;
    private final Cache<Integer, List<Long>> paginationFlightInfoCache;

    private final RuntimeService runtimeService;

    @Lazy
    @Autowired
    private IFlightQueryService flightQueryService;

    @Override
    @Slave
    public List<FlightInfoVO> queryAllFlight(Integer page) {
        CacheGetResult<List<Long>> cacheGetResult = paginationFlightInfoCache.GET(page);
        if (cacheGetResult.isSuccess()) {
            return cacheGetResult.getValue().stream().map(flightQueryService::queryOneFlight).toList();
        } else {
            Page<FlightPO> poPage = new Page<>(page, 5);
            List<Long> ids = new ArrayList<>();
            List<FlightInfoVO> result = flightMapper.selectList(poPage, new QueryWrapper<>()).stream()
                    .peek(flightPO -> ids.add(flightPO.getId()))
                    .sorted(Comparator.comparing(FlightPO::getStartTime))
                    .map(FlightConverter::convertToFlightInfoVO).toList();
            paginationFlightInfoCache.put(page, ids);
            return result;
        }
    }

    @Override
    @Cached(name = CacheConstants.FLIGHT_AMOUNT,key = "'all'")
    @Slave
    public Integer queryAllFlightAmount() {
        Long count = flightMapper.selectCount(new LambdaQueryWrapper<>());
        return count.intValue();
    }

    @Override
    @Slave
    public List<FlightInfoVO> queryIdleFlight(QueryFlightDTO queryFlightDTO) {
        LocalDate flightDate = queryFlightDTO.getFlightDate();
        if (flightDate.isBefore(LocalDate.now())) {
            throw new QueryException("时间不能早于当前时间");
        }

        CacheGetResult<List<Long>> cacheResult = specificFlightInfoCache.GET(queryFlightDTO.getCacheKey());
        if (cacheResult.isSuccess()) {
            return cacheResult.getValue().stream().map(flightQueryService::queryOneFlight).toList();
        } else {
            List<Long> ids = new ArrayList<>();
            List<FlightInfoVO> result = flightMapper.selectList(new LambdaQueryWrapper<FlightPO>()
//                            .eq(FlightPO::getStatus, FlightStatus.BOOKING.getType())
                            .eq(FlightPO::getDeployCity, queryFlightDTO.getDeployCity())
                            .eq(FlightPO::getArrivalCity, queryFlightDTO.getDestCity())
                            .between(FlightPO::getStartTime, TimeUtils.getDate(flightDate),TimeUtils.getDate(flightDate.plusDays(1))))
                    .stream()
                    .filter(flightPO -> {
                        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(String.valueOf(flightPO.getId())).includeProcessVariables().singleResult();
                        return Objects.equals(processInstance.getProcessVariables().get(SysConstants.STATUS), FlightStatus.BOOKING.getType());
                    })
                    .peek(flightPO -> ids.add(flightPO.getId()))
                    .sorted(Comparator.comparing(FlightPO::getStartTime))
                    .map(FlightConverter::convertToFlightInfoVO).toList();
            specificFlightInfoCache.put(queryFlightDTO.getCacheKey(), ids);
            return result;
        }
    }

    @Override
    @Slave
    public List<FlightInfoVO> querySimilarFlight(Long id) {
        FlightInfoVO flightInfoVO = flightQueryService.queryOneFlight(id);
        LocalDate startDate = LocalDate.ofInstant(flightInfoVO.getStartTime().toInstant(), ZoneId.systemDefault());
        String startCity = flightInfoVO.getDeployCity();
        String arrivalCity = flightInfoVO.getArrivalCity();

        return flightQueryService.queryIdleFlight(QueryFlightDTO.builder()
                .deployCity(startCity)
                .destCity(arrivalCity)
                .flightDate(startDate)
                .build()).stream().filter(flightInfo -> !flightInfo.getId().equals(flightInfoVO.getId())).toList();
    }

    @Override
    @Cached(name = CacheConstants.FLIGHT_INFO, key = "args[0]")
    @Slave
    public FlightInfoVO queryOneFlight(Long id) {
        FlightPO flightPO = flightMapper.selectOne(new LambdaQueryWrapper<FlightPO>().eq(FlightPO::getId, id));
        if (flightPO == null) {
            throw new QueryException("此航班不存在");
        }
        return FlightConverter.convertToFlightInfoVO(flightPO);
    }
}
